#include "StateManager.h"
#include "..\States\State.h"
#include "..\Generic\SFMLGameTime.h"
#include "RenderManager.h"
#include "..\Engines\RenderEngine.h"
#include "..\Generic\MessageSystem.h"
#include "PhysicsManager.h"
#include "..\Managers\InputManager.h"

StateManager::StateManager() : m_run(true) 
{
	//MessageSystem::getSingleton().registerSubject<StateManager>("quit", this, &StateManager::shutdownCallback);
}

StateManager::~StateManager() {clear();}

StateManager* StateManager::m_instance = nullptr;

StateManager& StateManager::getSingleton()
{
	if (m_instance == nullptr)
		m_instance = new StateManager;
	return *m_instance;
}

void StateManager::clean()
{
	if (m_instance != nullptr)
	{
		delete m_instance;
		m_instance = nullptr;
	}
}

void StateManager::setState(State* p_state)
{
	std::function<void()> func = [=] {
	if (!m_states.empty())
	{
		m_states.back()->shutdown();
		delete m_states.back();
		m_states.back() = nullptr;
		m_states.pop_back();
	}
	m_states.push_back(p_state);
	m_states.back()->initialize(); };
	m_stack.push_back(func);
}

void StateManager::pushState(State* p_state)
{
	std::function<void()> func =  [=] {
		if (!m_states.empty())
		{
			m_states.back()->setPause(true);
		}
		m_states.push_back(p_state);
		m_states.back()->initialize(); };
	m_stack.push_back(func);
}

void StateManager::popState()
{
	std::function<void()> func = [this] {
	if (!m_states.empty())
	{
		m_states.back()->shutdown();
		delete m_states.back();
		m_states.back() = nullptr;
		m_states.pop_back();
		if (!m_states.empty())
			m_states.back()->setPause(false);
	} };
	m_stack.push_back(func);
}

void StateManager::start()
{
	GameTime* gameTime = new SFMLGameTime;
	RenderEngine* renderEngine = RenderManager::getSingleton().getRenderEngine();
	while (m_run)
	{
		if (!m_stack.empty())
		{
			for (auto it = m_stack.begin(); it != m_stack.end(); it++)
				(*it)();
			m_stack.clear();
		}

		gameTime->update();
		PhysicsManager::getSingleton().flush();
		InputManager::getSingleton().capture();
		renderEngine->beginRender();
		for (auto it = m_states.begin(); it != m_states.end(); it++)
		{
			if (!(*it)->isPaused())
				(*it)->update(*gameTime);
		}
		PhysicsManager::getSingleton().step();
		PhysicsManager::getSingleton().syncSprites();
		renderEngine->render();
		//PhysicsManager::getSingleton().drawDebugData();
		renderEngine->endRender();
	}
	delete gameTime;
	gameTime = nullptr;
}

void StateManager::clear()
{
	if (!m_states.empty())
	{
		for (auto it = m_states.begin(); it != m_states.end(); it++)
		{
			(*it)->shutdown();
			delete (*it);
			(*it) = nullptr;
		}
		m_states.clear();
	}
}